Week 8

This week we were introduced to esp32 internet capabilities. I have decided i wanted to do 2 projects. A simple web-app which i will use to select modes on my final project. Than i wanted to try accessing some APIs using esp32.

First lets take a look at the app. Its fairly simple, but it was quite hard to make it run. First a created the web-app. Its just a header and some buttons. Here is the Header and first two buttons:

HTML code:

                
<!DOCTYPE HTML>
<html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

</head>
<body onload="loadButtons()">
<div class="main-title">
<h1>
    <span class="magic">LED MATRIX</span>
        </h1>
    </div>
    <div class="horizontal_separator"></div>
    <div class="empty_separator"></div>
    <div class="container">
        <button class="btn" id="gradient" onclick="resetPosition(this)">Smooth gradient</button>
    </div>
    <div class="container">
        <button class="btn" id="fourier" onclick="resetPosition(this)">Music reactive - Fourier</button>
    </div>
    <div class="container">
        <button class="btn" id="fU" onclick="resetPosition(this)">Music reactive - f/U</button>
    </div>
    <div class="container">
        <button class="btn" id="perlin" onclick="resetPosition(this)">Perlin noise</button>
    </div>
    <div class="container">
        <button class="btn" id="snake" onclick="resetPosition(this)">Snake game</button>
    </div>
    <script src="script.js"></script>
</body>
</html>
                
            

CSS code:

                
:root {  
--purple: rgb(123, 31, 162);
--violet: rgb(103, 58, 183);
--pink: rgb(244, 143, 177);
}

@keyframes background-pan{
from{
    background-position: 0% center;
}
to {
    background-position: -200% center;
}
}

body{
margin: 0;
font-family: Helvetica, sans-serif;
background: #1f1f1f;
display: flex;
min-height: 100vh;
justify-content: center;
align-items: center;
flex-direction: column;
}

* {
box-sizing: border-box;
}

p, small, ul{
color: #FEFFED;
}

p, ul{
font-size: 1.2em;
line-height: 28px;
}

.container{
width: 60vw;
height: 15vw;
display: flex;
background: none;
justify-content: center;
margin: 1em 0;
}
button{
padding: 8px 22px;
border-radius: 6px;
transition: all 0.2 ease;
}

button:active{
transform: scale(0.9);
}
button:hover{
background: linear-gradient(90deg, rgba(97,67,133,1) 100%, rgba(81,99,149,1) 0%);
}

.btn{
background: rgb(97,67,133);
background: linear-gradient(90deg, rgba(97,67,133,1) 0%, rgba(81,99,149,1) 100%);
width: 100%;
height: 100%;
color: #FEFFED;
padding: 0;
border: none;
font-size: 5vw;
}

.horizontal_separator{
width:100vw;
height: 5px;
background-color: #f1f1f1;
}
.empty_separator{
width: 100vw;
height:2vh;
background: none;
}

h1 {
margin: 0;
font-size: 12vw;
color: #f1f1f1;
}

.main-title{
top: 0;
display: flex;
width: 100vw;
height: 16vw;
justify-content: center;
background-color: #1f1f1f;

}

h1 > .magic{
animation: background-pan 3s linear infinite;
background: linear-gradient(
    to right,
    var(--purple),
    var(--violet),
    var(--pink),
    var(--purple)
);
background-size: 200%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
white-space: nowrap;
}         
                
            

And finally JavaScript:

                
var buttons;
var active_button = null;
function loadButtons(){
    buttons = document.getElementsByTagName('button');
}
function resetPosition(element){
    if(active_button != element.id){
    document.getElementById(element.id).style.background = "#f48fb1";
    for(let i = 0; i <buttons.length; i++){
        if(buttons[i].id != element.id){
            console.log(buttons[i].id)
            document.getElementById(buttons[i].id).style.background = '';
        }
    }
    active_button = element.id;
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/"+element.id, true);
    xhr.send();
    }
}
                
            

After clicking on button, we call get on new URL adress, which we can diferentiate on ESP32. ESP32 was set up as access point. Which means, other devices can connect to it. Its creating its own wifi signal. Here is arduino code:

                
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Arduino_JSON.h>
#include "SPIFFS.h"

// Replace with your network credentials
const char* ssid = "wifi-name";
const char* password = "wifi-password";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
IPAddress IP(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);


void initSPIFFS() {
    if (!SPIFFS.begin()) {
    Serial.println("An error has occurred while mounting SPIFFS");
    }
    Serial.println("SPIFFS mounted successfully");
}

// Initialize WiFi
void initWiFi() {
    WiFi.softAP(ssid, password);
    delay(500);
    WiFi.softAPConfig(IP, gateway, subnet);
    IPAddress IP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(IP);
}

void setup() {
    Serial.begin(115200);
    initWiFi();
    initSPIFFS();


    // Handle Web Server
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", "text/html");
    });

    server.serveStatic("/", SPIFFS, "/");

    server.on("/gradient", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("MODE:...Gradient");
    request->send(200, "text/plain", "OK");
    });

    server.on("/fourier", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("MODE:...Fourier");
    request->send(200, "text/plain", "OK");
    });

    server.on("/fU", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("MODE:...fU");
    request->send(200, "text/plain", "OK");
    });

    server.on("/perlin", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("MODE:...Perlin");
    request->send(200, "text/plain", "OK");
    });
    server.on("/snake", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("MODE:...Snake");
    request->send(200, "text/plain", "OK");
    });


    server.begin();
}

void loop() {
    
}
                
            

SPIFFS is library used for storing larger files on flash memory on esp32. I used it to store the html, css and javascript code. All the code does is that if GET request comes on some specific URL, we know which button was pressed and the button id is printed. Here it is working:

For the API project I found cool API from NASA website. Can be found - Here. The API i chose returns all asteroids which will fly-by Earth between dates specified in the GET request. It provides some more info, like how big the asteroid will be, or how was will it go.

First hard thing was to connect to schools eduroam wi-fi. I found this code. Which can be used almost immediately. All that is needed to be done is to replace "EAP_ANONYMOUS_IDENTITY" and "EAP_IDENTITY" with your email like "pechasim@fel.cvut.cz" and "EAP_PASSWORD" with your password for the eduroam website. Last but not least line "88" need to be commented out and line "90" needs to be uncommented. This will stop it from chicking certificate, which i had problem with. Although i downloaded the chools certificate. Now its all done. Here is the example of how it can look:

            
#include <WiFi.h> //Wifi library
#include "esp_wpa2.h" //wpa2 library for connections to Enterprise networks
#include <HTTPClient.h>
#include <ArduinoJson.h>
byte mac[6];
String url = "/eduroam/data.php"; //URL to target PHP file

#define EAP_ANONYMOUS_IDENTITY "pechasim@fel.cvut.cz" 
#define EAP_IDENTITY "pechasim@fel.cvut.cz" 
#define EAP_PASSWORD "my-password" //password for eduroam account

//SSID NAME
const char* ssid = "eduroam"; // eduroam SSID

void setup() {
    Serial.begin(115200);
    delay(10);
    Serial.print(F("Connecting to network: "));
    Serial.println(ssid);
    WiFi.disconnect(true);  //disconnect from WiFi to set new WiFi connection
    WiFi.mode(WIFI_STA); //init wifi mode

    WiFi.begin(ssid, WPA2_AUTH_PEAP, EAP_ANONYMOUS_IDENTITY, EAP_IDENTITY, EAP_PASSWORD); //WITHOUT CERTIFICATE - WORKING WITH EXCEPTION ON RADIUS SERVER

    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(F("."));
    }
    Serial.println("");
    Serial.println(F("WiFi is connected!"));
    Serial.println(F("IP address set: "));
    Serial.println(WiFi.localIP()); //print LAN IP
    WiFi.macAddress(mac);
    Serial.print("MAC address: ");
    Serial.print(mac[0], HEX);
    Serial.print(":");
    Serial.print(mac[1], HEX);
    Serial.print(":");
    Serial.print(mac[2], HEX);
    Serial.print(":");
    Serial.print(mac[3], HEX);
    Serial.print(":");
    Serial.print(mac[4], HEX);
    Serial.print(":");
    Serial.println(mac[5], HEX);
}
void loop() {
}
            
        

Now i added one API call to obtain current date, to look for asteroids for the day the code runs. And the i called the NASA API to receive the data. I than need to parse it and then print it. The call for the date needs to be done more times, because the API can sometimes hung up and not return anything. The final code looked like this:

            
#include <WiFi.h> //Wifi library
#include "esp_wpa2.h" //wpa2 library for connections to Enterprise networks
#include <HTTPClient.h>
#include <ArduinoJson.h>
byte mac[6];
String url = "/eduroam/data.php"; //URL to target PHP file

#define EAP_ANONYMOUS_IDENTITY "pechasim@fel.cvut.cz" 
#define EAP_IDENTITY "pechasim@fel.cvut.cz" 
#define EAP_PASSWORD "my-password" //password for eduroam account

// JSON parsing stuff
String serverName = "https://api.nasa.gov/neo/rest/v1/feed";
char currentDate[10];
bool date_obtained = false;
DynamicJsonDocument doc(50000);
DynamicJsonDocument date_doc(1000);

//SSID NAME
const char* ssid = "eduroam"; // eduroam SSID

void setup() {
    Serial.begin(115200);
    delay(10);
    Serial.print(F("Connecting to network: "));
    Serial.println(ssid);
    WiFi.disconnect(true);  //disconnect from WiFi to set new WiFi connection
    WiFi.mode(WIFI_STA); //init wifi mode

    WiFi.begin(ssid, WPA2_AUTH_PEAP, EAP_ANONYMOUS_IDENTITY, EAP_IDENTITY, EAP_PASSWORD); //WITHOUT CERTIFICATE - WORKING WITH EXCEPTION ON RADIUS SERVER

    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(F("."));
    }
    Serial.println("");
    Serial.println(F("WiFi is connected!"));
    Serial.println(F("IP address set: "));
    Serial.println(WiFi.localIP()); //print LAN IP
    WiFi.macAddress(mac);
    Serial.print("MAC address: ");
    Serial.print(mac[0], HEX);
    Serial.print(":");
    Serial.print(mac[1], HEX);
    Serial.print(":");
    Serial.print(mac[2], HEX);
    Serial.print(":");
    Serial.print(mac[3], HEX);
    Serial.print(":");
    Serial.print(mac[4], HEX);
    Serial.print(":");
    Serial.println(mac[5], HEX);

    // Get current Date 
    for(int i = 0; i < 5; ++i){
        Serial.print("Try to obtain date.... attempt: ");
        Serial.println(i+1);
        get_current_date();
        if(date_obtained){
          break;
        }
        delay(1000);
    }
    // If obtainenig date succesful - get asteroid data
      if(date_obtained){
       http_request();
      }
      Serial.println("Asteroids obtained");
}
void loop() {
}


void http_request() {
    //Check WiFi connection status
    if(WiFi.status()== WL_CONNECTED){
      HTTPClient http;

      String serverPath = serverName + "?api_key=DEMO_KEY&start_date=" + currentDate + "&end_date="+ currentDate;
      
      http.begin(serverPath.c_str());
      
      // Send HTTP GET request
      int httpResponseCode = http.GET();
      
      if (httpResponseCode>0) {
        Serial.print("HTTP Response code: ");
        Serial.println(httpResponseCode);
        String payload = http.getString();
        DeserializationError err = deserializeJson(doc, payload);
            if(!err){
                for (JsonObject asteroid : doc["near_earth_objects"][currentDate].as()) {

                    const char* asteroid_id = asteroid["id"];
                    Serial.print("Asteroid id: ");
                    Serial.println(asteroid_id);
                    double asteroid_diameter_km = asteroid["estimated_diameter"]["meters"]["estimated_diameter_max"];
                    Serial.print("diameter (m): ");
                    Serial.println(asteroid_diameter_km);
                    bool is_hazardous = asteroid["is_potentially_hazardous_asteroid"];
                    Serial.print("is hazardous: ");
                    Serial.println(is_hazardous);
                    JsonObject approach_data = asteroid["close_approach_data"][0];
                    
                    const char* kmh = approach_data["relative_velocity"]["kilometers_per_hour"];
                    const char* miss_dist = approach_data["miss_distance"]["kilometers"];
                    Serial.print("Approach speed (km/h): ");
                    Serial.println(kmh);
                    Serial.print("Miss distance (km): ");
                    Serial.println(miss_dist);
                    Serial.println("\n");
                }
            }
            else{
            Serial.print(F("deserializeJson() failed with code "));
            Serial.println(err.f_str());
            }   
        }  
      else {
        Serial.print("Error code: ");
        Serial.println(httpResponseCode);
      }
      // Free resources
      http.end();
    }
    else {
      Serial.println("WiFi Disconnected");
    }
}

void get_current_date(){
if(WiFi.status()== WL_CONNECTED){
      HTTPClient http;

      String serverPath ="http://worldtimeapi.org/api/timezone/Europe/Prague";
      
      http.begin(serverPath.c_str());
  
      // Send HTTP GET request
      int httpResponseCode = http.GET();
      
       if (httpResponseCode>0) {
        Serial.print("HTTP Response code: ");
        Serial.println(httpResponseCode);
        String payload = http.getString();
        DeserializationError err = deserializeJson(date_doc, payload);
            if(!err){
                const char* datetime = date_doc["datetime"];
                for(int i = 0; i < 10; ++i){
                currentDate[i] = datetime[i];
                }
                Serial.println(currentDate);
                date_obtained = true;
            }
            else{
            Serial.print(F("deserializeJson() failed with code "));
            Serial.println(err.f_str());
            }
        }  
      else {
        Serial.print("Error code: ");
        Serial.println(httpResponseCode);
      }
      // Free resources
      http.end();
    }
    else {
      Serial.println("WiFi Disconnected");
    }
}

            
        

I wanted to write the data to OLED display, but I didn't have time left and I couldn't finished it at the college dorm because I cannot connect more devices to the dorms wi-fi. So I just printed the data. Here is how it worked:

←Back